#ifndef G3_PLANE_H
#define G3_PLANE_H

#include "log.h"
#include "color.h"

#include "m_vector.h"

namespace G3 {

enum HALFSPACE {
     BEHIND = -1,
     NEGATIVE = -1,
     ON_PLANE = 0,
     POSITIVE = 1,
     IN_FRONT = 1,
     SPANNING = 2
};

template <class T>
class AXIS2_3 {
	public:
		VEC3<T> u, v;

		AXIS2_3<T>& operator= (const AXIS2_3<T> &A);
};

// TODO:: Make members more readable
template <class T>
class PLANE3 {
	public:
       	PLANE3 (T A = 0, T B = 0, T C = 1, T D = 0);
       	PLANE3 (const VEC3<T> &a, const VEC3<T> &b, const VEC3<T> &c);
       	PLANE3 (const VEC3<T> &N, const VEC3<T> &P);

       	VEC3<T> n, p;
       	T d;

       	PLANE3<T>& operator= (const PLANE3<T> &P);

//==============================================================================
// Normalizes the plane
//==============================================================================
       	void Normalize ();

//==============================================================================
// Gets the distance of a point from the plane
//==============================================================================
       	T GetDistance (const VEC3<T> &P) const;

//==============================================================================
// Where's the point? Behind the plane?
//==============================================================================
       	HALFSPACE ClassifyPoint (const VEC3<T> &P) const;

//==============================================================================
// Calculates the plane's surface axis
//==============================================================================
	AXIS2_3<T> CalcSurfaceAxis () const;

	void Render (COL3F Color, double Scale = 1) const;
};

//==============================================================================
// Checks to see if these two planes are parallel
//==============================================================================
template <class T>
__inline bool Parallel (const PLANE3<T> &Plane1, const PLANE3<T> &Plane2);

//==============================================================================
// Gets the distance between two parallel planes
//==============================================================================
template <class T>
__inline T GetDistance (const PLANE3<T> &Plane1, const PLANE3<T> &Plane2);

//==============================================================================
// Projects a vector onto a plane
//==============================================================================
template <class T>
__inline VEC3<T> ProjectOntoPlane (const PLANE3<T> &Plane, const VEC3<T> &P) {
	return (P - Plane.n * Plane.GetDistance (P));
}

//==============================================================================
// Gets the relative to plane 2d vector from a 3d vector
//==============================================================================
template <class T>
VEC2<T> ProjectRelativeToPlane (const PLANE3<T> &Plane, const VEC3<T> &P);

//==============================================================================
// Explicit template instantiation
//==============================================================================
extern template class AXIS2_3<int>;
extern template class AXIS2_3<float>;
extern template class AXIS2_3<double>;

extern template class PLANE3<int>;
extern template class PLANE3<float>;
extern template class PLANE3<double>;

extern template __inline bool Parallel (const PLANE3<int> &Plane1, const PLANE3<int> &Plane2);
extern template __inline bool Parallel (const PLANE3<float> &Plane1, const PLANE3<float> &Plane2);
extern template __inline bool Parallel (const PLANE3<double> &Plane1, const PLANE3<double> &Plane2);

extern template __inline int GetDistance (const PLANE3<int> &Plane1, const PLANE3<int> &Plane2);
extern template __inline float GetDistance (const PLANE3<float> &Plane1, const PLANE3<float> &Plane2);
extern template __inline double GetDistance (const PLANE3<double> &Plane1, const PLANE3<double> &Plane2);

extern template VEC2<int> ProjectRelativeToPlane (const PLANE3<int> &Plane, const VEC3<int> &P);
extern template VEC2<float> ProjectRelativeToPlane (const PLANE3<float> &Plane, const VEC3<float> &P);
extern template VEC2<double> ProjectRelativeToPlane (const PLANE3<double> &Plane, const VEC3<double> &P);

//==============================================================================
// Types
//==============================================================================
typedef AXIS2_3<int>		AXIS2_3I;
typedef AXIS2_3<float>		AXIS2_3F;
typedef AXIS2_3<double>		AXIS2_3D;

typedef PLANE3<int>		PLANE3I;
typedef PLANE3<float>	PLANE3F;
typedef PLANE3<double>	PLANE3D;

}

#endif //G3_PLANE_H
